package hn.cch.spring.aop.aspect.demo;

import hn.cch.spring.aop.MyMethodInvocation;
import hn.cch.spring.aop.aspect.ExecutionAspect;
import hn.cch.spring.aop.aspect.ExecutionTarget;
import org.aspectj.lang.annotation.Around;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectInstanceFactory;
import org.springframework.aop.aspectj.AspectJAroundAdvice;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.SingletonAspectInstanceFactory;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.aop.support.DefaultPointcutAdvisor;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class MethodInterceptorDemo {

    public static void main(String[] args) throws Throwable {
        // 适配器模式：所有通知都转化为环绕通知
        List<Advisor> advisors = getAdvisors();

        ExecutionTarget executionTarget = new ExecutionTarget();
        Method method = ExecutionTarget.class.getMethod("around", String.class);
        // 代理工厂
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.addAdvisors(advisors);
        proxyFactory.setTarget(executionTarget);
        // 单例模式，ThreadLocal<MethodInvocation>
        proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE);

        // ReflectiveMethodInvocation构造方法被protected修饰
        MyMethodInvocation methodInvocation = new MyMethodInvocation(proxyFactory.getProxy(), executionTarget, method, new Object[]{"xyz"}, ExecutionTarget.class, proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(method, ExecutionTarget.class));
        // 依次调用环绕通知
        methodInvocation.proceed();
    }

    private static List<Advisor> getAdvisors() {
        AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new ExecutionAspect());
        // Aspect -> Advisor
        List<Advisor> advisors = new ArrayList<>();
        for (Method method : ExecutionAspect.class.getDeclaredMethods()) {
            if (method.isAnnotationPresent(Around.class)) {
                // 切点
                String expression = method.getAnnotation(Around.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(expression);
                //   通知：Advice都实现MethodInterceptor，都转化为环绕通知
                AspectJAroundAdvice advice = new AspectJAroundAdvice(method, pointcut, factory);
                // 切面
                Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
                advisors.add(advisor);
            }
        }
        return advisors;
    }
}
